import org.junit.jupiter.api.*;

import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static org.junit.jupiter.api.Assertions.*;

@DisplayName("Проверка транзакций")
public class BankTest {
    private Bank bank;
    private Account account1;
    private Account account2;

    private ExecutorService executorService;

    @BeforeEach
    void beforeEach() {
        bank = new Bank();
        bank.addAccount(account1 = new Account("0001"));
        bank.addAccount(account2 = new Account("0002"));

        executorService = Executors.newFixedThreadPool(50);

        account1.setMoney(10_000_000);
        account2.setMoney(10_000_000);
    }

    @Test
    @DisplayName("Транзакции без проверки")
    void smallTransfers() throws InterruptedException {
        sendTransactions(200, 50_000, 40_000);
        bank.close();

        assertEquals(200, bank.getTransferCount());
        assertEquals(20_000_000, bank.getSumAllAccounts());
        assertEquals(9_000_000, bank.getBalance(account1.getAccNumber()));
        assertEquals(11_000_000, bank.getBalance(account2.getAccNumber()));
    }

    @Test
    @DisplayName("Транзакции с проверкой")
    void bigTransfers() throws InterruptedException {
        sendTransactions(10, 50_001, 40_001);
        Thread.sleep(5000);
        sendTransactions(10, 50_001, 40_001);
        bank.close();

        assertTrue(account1.isLocked());
        assertTrue(account2.isLocked());
        assertEquals(10, bank.getTransferCount());
        assertEquals(20_000_000, bank.getSumAllAccounts());
        assertEquals(9_950_000, bank.getBalance(account1.getAccNumber()));
        assertEquals(10_050_000, bank.getBalance(account2.getAccNumber()));
    }

    private void sendTransactions(int number, long amount12, long amount21) throws InterruptedException {
        Collection<Callable<Object>> tasks = new ArrayList<>();

        for (int i = 0; i < number; i++) {
            if (i % 2 == 0) {
                tasks.add(() -> {
                    bank.transfer(account1.getAccNumber(), account2.getAccNumber(), amount12);
                    return null;
                });
            } else {
                tasks.add(() -> {
                    bank.transfer(account2.getAccNumber(), account1.getAccNumber(), amount21);
                    return null;
                });
            }
        }

        executorService.invokeAll(tasks);
    }
}
